// file:include/os/task.h
// autor:jiangxinpeng
// time:2021.1.18
// copyright:(C) 2020-2050 by jiangxinpeng,All right are reserved.
#ifndef _OS_TASK_H
#define _OS_TASK_H

#include <arch/fpu.h>
#include <os/exception.h>
#include <os/alarm.h>
#include <os/fsal.h>
#include <os/pthread.h>
#include <os/spinlock.h>
#include <os/portcom.h>
#include <os/timer.h>
#include <os/vmm.h>
#include <sys/proc.h>
#include <lib/type.h>

// cpu loads level
#define CPU_TASK_LOAD_MAX ((float)0.7)
#define CPU_TASK_LOAD_WARN ((float)0.5)

// task balance timeout
#define TASK_BALANCE_TIMEOUT 10

// alloc kernel stack to every task
#define TASK_KERNEL_STACK_SIZE 8136
#define TASK_NAME_LEN 32
#define LPC_PORT_MAX 8

// task timeslice config
#define TASK_TIMESLICE_MIN 1
#define TASK_TIMESLICE_MAX 100
#define TASK_TIMESLICE_BASE 3

#define TASK_STACK_MAGIC 0x0616888
#define TASK_STACK_ARG_MAX 16

// init sysapp path
#define INIT_SYSAPP_PATH "/sbin/init"

typedef void (*exit_hook_t)(void *);

typedef enum sched_prior_level
{
    TASK_PRIOR_LEVEL_UNKNOW = 0,
    TASK_PRIOR_LEVEL_LOW,
    TASK_PRIOR_LEVEL_NORMAL,
    TASK_PRIOR_LEVEL_HIGH,
    TASK_PRIOR_LEVEL_REALTIME,
    TASK_PRIOR_LEVEL_MAX
} sched_prior_level;

typedef struct lpc_port_table
{
    void *port[LPC_PORT_MAX];
} lpc_port_table_t;

typedef enum
{
    TASK_READY = 0, // process ready finished
    TASK_RUNNING,   // process is running
    TASK_BLOCKED,   // proccess because some reason blocked
    TASK_WAITTING,  // process is waitting sub process
    TASK_STOPPED,   // process was stopped
    TASK_HANGING,   // process is hanging,wait father process recover.
    TASK_ZOMBIE,    // process is running in zombie status,father process no wait it.
} task_status_t;

typedef enum
{
    THREAD_DETACH = 1 << 0,             // thread detch status,indiate itself had free source
    THREAD_JOINED = 1 << 1,             // thread was waitting by other thread
    THREAD_JOINNING = 1 << 2,           // thread is waitting other thread
    THREAD_CANCEL_DISABLE = 1 << 3,     // thread no can cancel
    THREAD_CANCEL_ASYCHRONOUS = 1 << 4, // thread exit on times while was cancel
    THREAD_CANCELED = 1 << 5,           // thread had cancaled
    THREAD_WAITLIST = 1 << 6,           // thread in wait list
    THREAD_KERNEL = 1 << 7,             // thread in kernel
    THREAD_CANCEL_ENABLE = 1 << 8,      // thread can be cancel
} thread_flags_t;

// task struct block
typedef struct task
{
    uint8_t *kstack;      // kernel stack
    task_status_t status; // task status
    spinlock_t lock;
    cpuid_t cpuid;      // processor ID
    cpuid_t last_cpuid; // last dispatch processor ID

    pid_t pid;               // process id
    pid_t parent_pid;        // father id
    pid_t threadgroup_id;    // thread group id
    pid_t processgroup_id;   // process group id
    uid_t uid;               // task UID
    gid_t gid;               // task gid
    thread_flags_t flags;    // thread flags
    uint8_t priority;        // task dynamic priority
    uint8_t static_priority; // task static priority
    uint32_t ticks;          // less timeslice
    uint32_t timeslice;      // timeslice,can dynamic change
    uint32_t elapsed_ticks;  // task had used timeslice
    uint32_t syscall_ticks;  // syscall had used timslice
    uint32_t syscall_ticks_delta;
    int exit_status;
    char name[TASK_NAME_LEN];              // task name
    vmm_t *vmm;                            // task virtual mem manage
    list_t list;                           // task queue list,ready queue or blocked queue
    list_t global_list;                    // global task list
    exception_manager_t exception_manager; // task exception manager
    timer_t sleep_time;                    // task sleep time
    alarm_t alarm;                         // alarm
    uint32_t errno;                        // error code
    pthread_desc_t *pthread;               // user pthread manage
    file_manager_t *fileman;               // file manager
    exit_hook_t exit_hook;                 // exit called hook function
    void *exit_hook_arg;
    lpc_port_table_t port_table; // lpc port table
    port_com_t *port_comm;       // port comm
    fpu_t fpu;                   // fpu
    uint32_t stack_magic;        // stack magic number,used to ack stack true
} task_t;

typedef struct
{
    uint32_t ebp;
    uint32_t ebx;
    uint32_t edi;
    uint32_t esi;

    void (*eip)(task_func_t fun, void *arg);
    uint32_t unused;
    task_func_t function;
    void *arg;
} thread_stack_t;

extern list_t task_global_list;
extern volatile int task_init_done;

// kernel thread
#define TASK_IS_KERNEL_THREAD(task) ((task)->flags & THREAD_KERNEL)
// single thread
#define TASK_IS_SINGAL_THREAD(task) ((((task)->pthread && (AtomicGet(&(task)->pthread->thread_count) <= 1)) || ((task)->pthread == NULL)) && (!TASK_IS_KERNEL_THREAD(task)))
// task status stop
#define TASK_WAS_STOPPED(task) ((task)->status == TASK_STOPPED)
#define TASK_NOT_READY(task) ((task)->status == TASK_BLOCKED || (task)->status == TASK_WAITTING || (task)->status == TASK_STOPPED)
#define TASK_INTO_WAITLIST(task) ((task)->flags |= THREAD_WAITLIST)
#define TASK_EXIT_WAITLIST(task) ((task)->flags &= (~THREAD_WAITLIST))
#define TASk_IN_WAITLIST(task) ((task)->flags & THREAD_WAITLIST)
// two tasks in same  thread group
#define TASK_IN_SAME_THREAD_GROUP(a, b) ((a)->threadgroup_id == (b)->threadgroup_id)

// check thread cancelation
#define TASK_CHECK_THREAD_CANNELATION(task)                                            \
    if (((task)->flags & THREAD_CANCELED) && (!(task)->flags & THREAD_CANCEL_DISABLE)) \
    {                                                                                  \
        PthreadExit(THREAD_CANCELED);                                                  \
    }

// user init process id
#define USER_INIT_PROCESS_ID 1
// task get trap frame
#define TASK_GET_TRAP_FRAME(task) ((trap_frame_t *)((uint8_t *)(task) + TASK_KERNEL_STACK_SIZE - sizeof(trap_frame_t)))

void TaskInitKernel();
void TaskInit(task_t *task, char *name, uint8_t level);
void TaskActive(task_t *task);
void TaskBlock(int status);
void TaskUnBlock(task_t *task);
void TaskReady(task_t *task);
int TaskTakePid();
int TaskRollBackPid();
void TaskYield();
void TaskFree(task_t *task);
pid_t TaskGetPid(task_t *task);
pid_t SysGetPid();
pid_t SysGetParPid();
pid_t SysGetThreadPid();
int SysSetProcessGroupId(pid_t pid, pid_t pgid);
int SysGetProcessGroupId(pid_t pid);
int SysTaskStatus(tstatus_t *ts, uint32_t *index);
void SysExit(int status);
int SysGetVer(char *buff, int len);
void SysTaskYield();

int TaskCountChild(task_t *parent);

int TaskWakeUp(task_t *task);
task_t *TaskFindByPid(pid_t pid);
task_t *TaskFindByName(char *name);
int TaskIsChild(pid_t pid, pid_t child_pid);
task_t *TaskCreate(char *name, uint32_t prior, task_func_t fun, void *arg);
void TaskExit(int status);

int TaskCancelTimer(task_t *task);
void TaskAddToGlobalList(task_t *task);

int TaskStackBuildWhenForking(task_t *child);
void TaskStartUser();

int TaskSetCwd(task_t *task, const char *path);

void TaskPrint();

void TaskBlockTarge(task_t *task, int status);

static inline void TaskExitHook(task_t *task)
{
    if (task->exit_hook)
    {
        task->exit_hook(task->exit_hook_arg);
        task->exit_hook = NULL;
    }
}

#endif